home *** CD-ROM | disk | FTP | other *** search
- Subject: v13i045: Convert (some) csh scripts to ksh scripts
- Newsgroups: comp.sources.unix
- Sender: sources
- Approved: rsalz@uunet.UU.NET
-
- Submitted-by: Bob Mcqueer <rtech!bobm>
- Posting-number: Volume 13, Issue 45
- Archive-name: korner
-
- [ As Bob admits, this is a hack. Still and all, it might help folks make
- the great leap from csh to ksh. I wouldn't know, and haven't tried
- this. --r$ ]
-
- This program feeds CSH scripts to a copy of /bin/csh then analyzes
- the new aliases and variables that have been created, turning them
- into (nearly) equivalent KSH shell functions.
-
- {amdahl, sun, mtxinu, hoptoad, cpsc6a}!rtech!bobm
- ------ cut here -------
- #! /bin/sh
- # This is a shell archive, meaning:
- # 1. Remove everything above the #! /bin/sh line.
- # 2. Save the resulting text in a file.
- # 3. Execute the file with /bin/sh (not csh) to create the files:
- # read.me
- # Makefile
- # korner.1
- # diagnostic.c
- # korner.c
- # strtok.c
- # func
- # This archive created: Sun Oct 25 11:39:24 1987
- export PATH; PATH=/bin:$PATH
- if test -f 'read.me'
- then
- echo shar: will not over-write existing file "'read.me'"
- else
- cat << \SHAR_EOF > 'read.me'
-
- The problem this stuff addresses:
-
- I find myself the lone ksh user in a csh community. The tools
- I have to use on a daily basis are set up by a set of csh "source"
- files, which define some of those tools as csh aliases. But,
- dadburn it, I want to use ksh! AND I don't want to have to keep
- redoing my own setup files everytime somebody does something to
- the "standard" environment setup files which everybody is supposed
- to "source". Take it as a given that I'm not going to convince
- anybody that the tool set should be defined in such a way that it
- is independent of which shell you run (the tools in question are
- often aliases for things to convert your environment on the fly -
- stuff like "eval `<gargle>`" where <gargle> is some script that
- generates sources and setenv's).
-
- What I need is to be able to "source" csh scripts under ksh, doing
- the same thing in my ksh environment that the script intended to
- do under csh.
-
- What we got here:
-
- What I do is define a ksh function for "source" which:
-
- 1) uses csh to run the source file, dumping the resultant aliases,
- variables, directory stack, etc. off to a temp file.
-
- 2) runs a translator (the program "korner") over the temp file,
- producing a ksh file to run with "." Korner is in no way
- shape or form intended to handle all the csh constructs
- which could be shoveled into aliases - but it does know
- how to convert simpler argument metasyntax.
-
- 3) does a "." of the file produced in 2), and removes the temp's.
-
- There are LOTS of holes here - the thing is basically a hack - but I've
- been using it and having it work. Doing step 1) alleviates a LOT of
- the problem - let csh figure out its own silly script files and you
- only have to understand what finds its way into the types of aliases
- you need to translate. A pitfall, of course, would be source files
- depending on environmental things set up by preceding source files
- which don't get passed from ksh into csh (cdpath setting, previous
- aliases, etc). You would then have to make a source file of your
- own which sourced all the others, thus letting csh setup the whole
- mess all over again.
-
- This approach even seems to work fast enough to use it on the fly
- as I do - you could also save the output to periodically generate
- your own setup files.
-
- "func" contains my definition for the "source" function. The variable
- KD is defined for use in temp file names. If you define the variable
- KDOUT, the generated ksh "." files will get saved in there. For my
- purposes, I also need to recognize "setenv" and "unsetenv" for the
- generated aliases. You will note that it filters the printenv output
- which gets to the translator. The "source" files I'm using this on do
- "setenv"'s of around 100 variables. Not filtering out the ones that
- the csh "source" didn't change was causing ksh to run out of space
- for variable assignments by reassigning them all the time.
-
- korner.1 is the man page for "korner". The "strtok.c" source
- file may be dispensed with if you have a system strtok() call
- available.
-
- To process the csh directory stack, it is expected that you have
- defined a "pushd" in your ksh definitions. See korner.1.
-
- caveats:
-
- I made no attempt to be comprehensive. I just did a quick look over
- the csh man page, and translated the metasyntax that seemed reasonable
- to translate in processing the aliases.
-
- It's strictly a hand tooled parser - no formal grammar definition or
- anything like that.
-
- I've debugged this only to the extent that it works on the csh source
- files I've had to use.
-
- It's only ever run in one environment - Pyramid OSx, generally in the
- ucb universe, with what I believe is an early ksh version. I haven't
- had occasion to try this thing on other machines / versions of shells,
- etc. Some things, such as the expected output format of the various
- csh commands, were determined simply by examining what the local csh
- did, and slight differences in somebody else's csh might break it.
-
- Because of the above, it will likely have to be tweaked a bit to
- get YOUR set of csh setup files to run through it.
-
- Let me know of significant improvements, fixes, etc.
-
- Bob McQueer
- {amdahl, sun, mtxinu, hoptoad, cpsc6a}!rtech!bobm
- SHAR_EOF
- fi # end of overwriting check
- if test -f 'Makefile'
- then
- echo shar: will not over-write existing file "'Makefile'"
- else
- cat << \SHAR_EOF > 'Makefile'
- OBJS = korner.o diagnostic.o strtok.o
-
- CFLAGS = -O
-
- korner: $(OBJS)
- cc -o korner $(OBJS)
- SHAR_EOF
- fi # end of overwriting check
- if test -f 'korner.1'
- then
- echo shar: will not over-write existing file "'korner.1'"
- else
- cat << \SHAR_EOF > 'korner.1'
- .tr ~
- .TH KORNER LOCAL 2/1/86
- .UC
- .SH NAME
- korner - ksh environment converter
- .SH SYNOPSIS
- .B korner
- [-dpushd] [-ssectchar] [-c] [-x] [-p] [-l] [-|infile] [outfile]
- .SH DESCRIPTION
- .I Korner
- takes input describing environment settings in formats typically produced by
- .I csh (1)
- and
- .I printenv (1)
- and produces ksh commands to reflect them. The purpose is to allow ksh users
- to use csh "source" files, obtaining a ksh environment duplicating that set up
- for csh.
- .sp
- The -c option will cause the ksh CDPATH to be replaced rather than
- prefixed. It is normally prefixed because csh does not pick up the
- ksh CDPATH value as its own setting, the way it does the PATH setting.
- .sp
- The -x option will cause
- .I korner
- NOT to export the variables it assigns from its
- .I printenv
- style input. Export is the default because this is implied by use
- of the csh "setenv" builtin, and if the input really comes from
- .I printenv,
- the variables have to be exported for the
- .I printenv
- process to see them anyway.
- .sp
- The -p option will allow csh "prompt" settings to affect the ksh PS1 variable.
- Normally, these are thrown away since csh doesn't inherit the ksh prompt, and
- you don't want PS1 turned into the default csh "%".
- .sp
- The
- .I -d
- option will cause
- .I korner
- to use an alternate "pushd" command when processing the directory stack.
- The directory stack environment input is reproduced by generating a series
- of "pushd"'s, or the specified command.
- .sp
- The -l option causes unrecognized csh "set"'s to be assigned as unexported
- ksh variables. They are normally ignored.
- .sp
- The -s option specifies some character other than the default "+" used
- to separate sections of
- .I korner
- input. This is useful if the environment being processed insists on
- naming something "+".
- .sp
- If the input file is missing or given as "-", standard input is used.
- .sp
- If the output file is missing standard output is used.
- .SH INPUT~FORMAT
- Input to
- .I korner
- is in several sections, each beginning
- with a line starting with a "+" ( or the alternate character specified
- by the -s option). Lines following the section delimiter are understood
- to represent a certain type of environment information until the next "+"
- line, or end of file.
- .sp
- The "+E" line signifies variable assignments in the format produced by the
- .I printenv
- command. They will be converted into pairs of export and variable
- assignment statements. If -x is set, the exports will not be done.
- .sp
- The "+A" line signifies csh
- aliases in the format produced by the "alias" listing.
- A ksh alias or function with the same name will be produced for each.
- If no quoting or arguments are required, an alias is produced, otherwise
- a function which attempts to mimic the
- csh alias using ksh argument syntax.
- .sp
- The "+D" line should actually only have one line following it, which is
- a directory stack, ala the csh "dirs" command. The first word is assumed
- to be the current directory, and is thrown out if a "cwd" setting has been seen.
- The rest are used in reverse order
- to generate "pushd" commands for the ksh directory stack.
- .sp
- The "+S" signifies the results of a csh "set" command. Selected items
- from the list which have ksh analogs are processed into settings of the
- appropriate ksh variable. "cwd" is used to set the current directory via a "cd"
- command. Use of "prompt" is optional, based on the "-p" flag. "cdpath" is
- usually prefixed to an existing
- ksh CDPATH because invoking csh under ksh doesn't get the csh cdpath set
- up from the ksh one, as happens with "path" from PATH. Using command line
- option "-c" makes the "cdpath" setting replace any existing CDPATH.
- Settings not recognized as something with a ksh analog are usually ignored, but
- may be turned into unexported variable assignments with the "-l" option.
- .sp
- A "+E" section should precede a "+S" section so that the csh setting of
- things like "home" can override an exported-in HOME variable. A "+S"
- section should precede a "+D" section so that the HOME setting, if
- changed, will be correct for interpretation of tilde's.
- .sp
- The "cd" command generated by
- .I korner
- based on either the "cwd" setting or the start of the "+D" section
- is deferred to the end of its output.
- .SH DIAGNOSTICS
- Messages printed on standard error for failures and illegal input.
- .SH BUGS~AND~LIMITATIONS
- There are many things which cannot be handled for a variety of reasons.
- Attempts have been made to at least generate diagnostics for these cases.
- .sp
- It is possible to define variables with strange
- names, like "&", and actually get them into the environment table for
- .I printenv
- to see. These will be thrown out with an error message. Note that shell
- processed "variables" like "$#" don't turn up in
- .I printenv.
- .I Korner
- wants variable names beginning with a underscores or an alphabetic, and
- containing underscores and alphanumerics.
- .sp
- Variable assignments containing embedded newlines will be handled provided
- there is not an "=" somewhere after the newline. In this situation, it
- is impossible to tell from the output of
- .I printenv
- whether you have a single variable containing an embedded newline, or
- two variable assignments.
- .I Korner
- simply assumes the latter, rather than trying to
- guess whether you REALLY have a variable named " ", or <newline><space><equals>
- was embedded in the preceding definition.
- .sp
- The metasyntax used by csh for describing aliases doesn't match up
- exactly with ksh function argument syntax, and involved uses of it won't work.
- .I Korner
- attempts to translate what it can into ksh arguments, and produces
- diagnostics for constructions that it doesn't understand.
- .sp
- Embedding csh built-ins in alias definitions will not work unless ksh
- has similar built-ins, or definitions are made for them. "setenv"
- and "unsetenv" are two good examples.
- .I Korner
- does not attempt to generate diagnostics concerning built-ins. The intent
- is that you will define ksh aliases / functions for the ones you need.
- .sp
- In particular, csh "flow-of-control" in aliases won't work, since
- .I korner
- does not know about "if", "else" "foreach", etc.
- .sp
- Embedded newlines inside alias definitions will cause problems. Fortunately,
- csh does not reflect what kind of white space appeared in the alias definition,
- only producing a newline in the alias definition if there is a literal
- newline to be inserted somewhere.
- .sp
- Even with these severe limitations, the author has found that
- .I korner
- suffices to allow him to use tools and environment setup provided
- as csh "source" files under ksh. Many csh "source" scripts only use
- complex csh interpretation to set up an environment, not to define
- aliases. Simply allowing csh to do so, then "dumping" the result for
- .I korner
- to analyze gets around a lot of translation problems.
- .SH AUTHOR
- R. L. McQueer
- SHAR_EOF
- fi # end of overwriting check
- if test -f 'diagnostic.c'
- then
- echo shar: will not over-write existing file "'diagnostic.c'"
- else
- cat << \SHAR_EOF > 'diagnostic.c'
- #include <stdio.h>
-
- /*
- ** generic error message routines. Diag_xxx, may be externally set by caller.
- **
- ** possible portability problem - use of several "long" arguments to pass
- ** stack through to underlying printf() family routine.
- */
-
- /*
- **
- ** Copyright (c) 1987, Robert L. McQueer
- ** All Rights Reserved
- **
- ** Permission granted for use, modification and redistribution of this
- ** software provided that no use is made for commercial gain without the
- ** written consent of the author, that all copyright notices remain intact,
- ** and that all changes are clearly documented. No warranty of any kind
- ** concerning any use which may be made of this software is offered or implied.
- **
- */
-
- char *Diag_file = ""; /* filename for use in diagnostic message */
- int Diag_line = 1; /* diagnostic line number */
- FILE *Diag_fp = stderr; /* output stream for messages */
- char *Diag_cmd = "?"; /* command name for fatal() / usage() */
-
- static int (*Fatcall)() = NULL;
-
- /*
- ** print nonfatal diagnostic with line number from an input file. Format
- ** compatible with "context"
- */
- diagnostic(s,a,b,c,d,e,f)
- char *s;
- long a,b,c,d,e,f;
- {
- fprintf(Diag_fp,"%s line %d: ",Diag_file,Diag_line);
- fprintf(Diag_fp,s,a,b,c,d,e,f);
- fprintf(Diag_fp,"\n");
- }
-
- /*
- ** print fatal error message and exit. May call user set cleanup routine first.
- ** argument list passed to fatal() will also be passed to cleanup routine.
- */
- fatal (s,a,b,c,d,e,f)
- char *s;
- long a,b,c,d,e,f;
- {
- fprintf (Diag_fp,"%s: ",Diag_cmd);
- fprintf (Diag_fp,s,a,b,c,d,e,f);
- fprintf (Diag_fp,"\n");
- if (Fatcall != NULL)
- (*Fatcall) (s,a,b,c,d,e,f);
- exit (1);
- }
-
- /*
- ** set cleanup routine for fatal() calls
- */
- fat_set (fn)
- int (*fn) ();
- {
- Fatcall = fn;
- }
-
- /*
- ** print usage message and exit.
- */
- usage (s,a,b,c,d,e,f)
- char *s;
- long a,b,c,d,e,f;
- {
- fprintf (Diag_fp,"usage: %s ",Diag_cmd);
- fprintf (Diag_fp,s,a,b,c,d,e,f);
- fprintf (Diag_fp,"\n");
- exit (1);
- }
- SHAR_EOF
- fi # end of overwriting check
- if test -f 'korner.c'
- then
- echo shar: will not over-write existing file "'korner.c'"
- else
- cat << \SHAR_EOF > 'korner.c'
- /*
- **
- ** Copyright (c) 1987, Robert L. McQueer
- ** All Rights Reserved
- **
- ** Permission granted for use, modification and redistribution of this
- ** software provided that no use is made for commercial gain without the
- ** written consent of the author, that all copyright notices remain intact,
- ** and that all changes are clearly documented. No warranty of any kind
- ** concerning any use which may be made of this software is offered or implied.
- **
- */
-
- #include <stdio.h>
- #include <ctype.h>
- #include <sys/param.h>
-
- char Pwd[MAXPATHLEN];
- char *Pushd = "pushd";
- char *Umsg = "[-dpushd] [-ssectionchar] [-c] [-x] [-p] [-l] [-|infile] [outfile]";
- char Sect = '+';
-
- int Cdreplace = 0;
- int Doprompt = 0;
- int Xvar = 1;
- int Lvar = 0;
-
- /*
- ** limits. BIGBUFFER determines how much text for a single alias, shell
- ** variable, etc. can be handled. DIRDEPTH determines the depth of csh pushd's
- ** which can be accomodated.
- */
- #define BIGBUFFER (MAXPATHLEN < 1800 ? 1800 : MAXPATHLEN)
- #define DIRDEPTH 120
-
- /*
- ** characters significant inside double quotes, thus requiring backslashes
- */
- #define DQSIG "\\$`\""
-
- extern char *Diag_cmd;
- extern char *Diag_file;
- extern int Diag_line;
-
- main(argc,argv)
- int argc;
- char **argv;
- {
- FILE *fpin;
- FILE *fpout;
- char *rindex();
-
- /*
- ** set command for messages
- */
- if ((Diag_cmd = rindex(*argv,'/')) == NULL)
- Diag_cmd = *argv;
- else
- ++Diag_cmd;
- ++argv;
-
- /*
- ** check options
- */
- while (argc > 1 && **argv == '-' && *((*argv)+1) != '\0')
- {
- --argc;
- for (++(*argv); **argv != '\0'; ++(*argv))
- {
- switch(**argv)
- {
- case 'x':
- Xvar = 0;
- break;
- case 'c':
- Cdreplace = 1;
- break;
- case 'l':
- Lvar = 1;
- break;
- case 'p':
- Doprompt = 1;
- break;
- case 'd':
- ++(*argv);
- if (**argv == '\0')
- {
- if ((--argc) < 1)
- usage(Umsg);
- ++argv;
- }
- Pushd = *argv;
- *argv += strlen(*argv) - 1;
- break;
- case 's':
- ++(*argv);
- if (**argv == '\0')
- {
- if ((--argc) < 1)
- usage(Umsg);
- ++argv;
- }
- Sect = **argv;
- *argv += strlen(*argv) - 1;
- break;
- default:
- usage(Umsg);
- }
- }
- ++argv;
- }
-
- /*
- ** set input stream, point Diag_file to name for diagnostics
- */
- if (argc > 1 && strcmp(*argv,"-") != 0)
- {
- if ((fpin = fopen(*argv,"r")) == NULL)
- fatal("Can't open %s",*argv);
- Diag_file = *argv;
- }
- else
- {
- fpin = stdin;
- Diag_file = "<STDIN>";
- }
-
- /*
- ** set ouput stream.
- */
- if (argc > 2)
- {
- ++argv;
- if ((fpout = fopen(*argv,"w")) == NULL)
- fatal("Can't open %s",*argv);
- }
- else
- fpout = stdout;
-
- parse(fpin,fpout);
- }
-
- /*
- ** output string intended to be inside double quotes, thus requiring
- ** backslashes
- */
- dq_out(f,str)
- FILE *f;
- char *str;
- {
- char *sp;
- char sc;
-
- for (;;)
- {
- for (sp = str; *sp != '\0' && index(DQSIG,*sp) == NULL; ++sp)
- ;
- if (*sp != '\0')
- {
- sc = *sp;
- *sp = '\0';
- fprintf(f,"%s\\%c",str,sc);
- str = sp+1;
- continue;
- }
- break;
- }
- fprintf(f,"%s",str);
- }
-
- /*
- ** generic variable assignment
- */
- var_assign(f,name,setting)
- FILE *f;
- char *name;
- char *setting;
- {
- fprintf(f,"%s=\"",name);
- dq_out(f,setting);
- fprintf(f,"\"\n");
- }
-
- /*
- ** find the starting delimiter of a name assignment, or return NULL
- ** designed to fail for wierd names that won't make proper
- ** ksh names
- */
- char *
- namstart(str,delim)
- char *str;
- char delim;
- {
- char *eq;
-
- if (*str != '_' && !isalpha(*str))
- return (NULL);
-
- for (eq=str+1; *eq != delim; ++eq)
- {
- if (*eq == '\0')
- return (NULL);
- if (*eq != '_' && !isalnum(*eq))
- return (NULL);
- }
-
- return (eq);
- }
-
- /*
- ** process input from various enviroment commands, writing out ksh
- ** commands to simulate that environment.
- **
- ** Input consists of lines beginning with "+" and a code which signifies
- ** what all following lines up to the next leading "+" contain:
- **
- ** +E:
- ** environment variables, printed as <var>=<string>, as from
- ** the "printenv" command. Turned into pairs of exports and
- ** variable assignments.
- **
- ** +S:
- ** csh "set" command results. Specific variables which have
- ** ksh analogs (term, path, cdpath, home, cwd) handled specially,
- ** others turned into straight variable assignments. The
- ** expected format is <name><tab><def>
- **
- ** +D:
- ** followed by a directory stack list, as from the csh "dirs"
- ** command. Turned into pushd commands. The directory stack
- ** is assumed to be separated by spaces, with pwd as the first
- ** item, followed by the top of the stack.
- **
- ** +A:
- ** csh style aliases. Turned into ksh aliases and functions,
- ** as much as possible. The expected format is <name><tab><def>.
- **
- ** Note that the "+S" input should precede the "+D" input for the latter to
- ** interpret ~ properly if "home" has been changed. "+E" should also precede
- ** the "+S" section to allow csh set commands to override ksh variables
- ** imported into another environment.
- */
- static
- parse(fin,fout)
- FILE *fin;
- FILE *fout;
- {
- char state;
- char bufr[BIGBUFFER+1];
- char *index();
- char *ptr;
- char *slash;
- char sc;
- int nolook;
-
- state = ' ';
- Diag_line = 0;
- Pwd[0] = '\0';
-
- /*
- ** we keep a lookahead record when needed rather than seeking back
- ** because we may be reading from stdin.
- */
- nolook = 1;
- for (;;)
- {
- if (nolook)
- {
- if (fgets(bufr,BIGBUFFER,fin) == NULL)
- break;
- bufr[strlen(bufr)-1] = '\0';
- ++Diag_line;
- }
- else
- nolook = 1;
- if (bufr[0] == Sect)
- {
- state = bufr[1];
- if (index("ESAD",state) == NULL)
- diagnostic("Unknown section type: %c%c",
- Sect, state);
- continue;
- }
- switch (state)
- {
- case 'E':
- if ((ptr = namstart(bufr,'=')) == NULL)
- {
- diagnostic("Bad variable definition");
- break;
- }
-
- *ptr = '\0';
- if (Xvar)
- fprintf(fout,"export %s\n%s=\"",bufr,bufr);
- else
- fprintf(fout,"%s=\"",bufr);
- ++ptr;
-
- /*
- ** Read ahead a record, and if it doesn't appear
- ** to be a new variable assignment or a state change,
- ** stick in backslash-newline and continue processing
- */
- for (;;)
- {
- dq_out(fout,ptr);
- if (fgets(bufr,BIGBUFFER,fin) == NULL)
- break;
- bufr[strlen(bufr)-1] = '\0';
- ++Diag_line;
-
- /*
- ** we don't use namstart here because it's
- ** better to have a odd variable name fail
- ** than have it appended to a good assignment.
- ** Of course, now we miss legitimate '='
- ** characters following embedded newlines
- */
- if (bufr[0] == Sect || index (bufr,'=') != NULL)
- {
- nolook = 0;
- break;
- }
- fprintf(fout,"\\\n");
- ptr = bufr;
- }
- fprintf(fout,"\"\n");
- break;
- case 'A':
- if ((ptr = namstart(bufr,'\t')) == NULL)
- {
- diagnostic("Bad alias name");
- break;
- }
- *ptr = '\0';
- ++ptr;
- al_parse(bufr,ptr,fout);
- break;
- case 'S':
- if ((ptr = namstart(bufr,'\t')) == NULL)
- {
- diagnostic("Bad \"set\" name");
- break;
- }
- *ptr = '\0';
- ++ptr;
- set_check(bufr,ptr,fout);
- break;
- case 'D':
- dir_list(bufr,fout);
- break;
- default:
- break;
- }
- }
-
- /*
- ** If we had pwd settings, issue a cd as the last thing
- */
- if (Pwd[0] != '\0')
- fprintf(fout,"cd %s\n",Pwd);
- }
-
- /*
- ** perform pushd's to get csh dir stack on ksh - must be pushed in reverse
- ** order. Having a limit built in is hardly elegant, but in practical
- ** terms, its hard to imagine directory stacks being useful after getting
- ** past a few directories deep. Plus, a rather large limit is cheap in terms
- ** storage.
- */
- static
- dir_list(s,f)
- char *s;
- FILE *f;
- {
- char *ptr;
- char *strtok();
- char *dir[DIRDEPTH];
- int numdir;
-
- /*
- ** first item is pwd. If we haven't set this because of a prior
- ** setting (like "cwd" in a +S section), do so.
- */
- ptr = strtok(s," ");
- if (ptr != NULL && Pwd[0] == '\0')
- strcpy(Pwd,ptr);
-
- numdir = 0;
- for (ptr = strtok(NULL," "); ptr != NULL; ptr = strtok(NULL," "))
- {
- if (numdir >= DIRDEPTH)
- {
- diagnostic("directory stack too deep - truncating");
- break;
- }
- dir[numdir] = ptr;
- ++numdir;
- }
-
- /*
- ** push in reverse order to get onto ksh stack
- ** We assume pushd handles ~ notation. As long as csh set
- ** command is processed before directory stack, changes of
- ** HOME will also be taken care of.
- */
- for (--numdir; numdir >= 0; --numdir)
- fprintf (f,"%s %s\n",Pushd,dir[numdir]);
- }
-
- /*
- ** handle a csh alias
- */
- static
- al_parse(name,cdef,f)
- char *name;
- char *cdef;
- FILE *f;
- {
- int wantfunc;
- char kdef[BIGBUFFER];
-
- if (! al_xln(cdef, kdef, &wantfunc))
- {
- /*
- ** explicit diagnostic for first error will have also
- ** been produced.
- */
- diagnostic("ALIAS %s=\"%s\" can't be translated\n",name,cdef);
- return;
- }
-
- if (wantfunc)
- fprintf(f,"function %s\n{\n\t%s\n}\n",name,kdef);
- else
- {
- fprintf(f,"alias %s=\"",name);
- dq_out(f,kdef);
- fprintf(f,"\"\n");
- }
- }
-
- /*
- ** translate subset of csh alias syntax into ksh. If arguments or quotes
- ** are required, returned flag is TRUE, indicating need to define a function
- ** rather than an alias. Diagnostics produced before error returns.
- */
- static
- al_xln(in,out,flag)
- char *in, *out;
- int *flag;
- {
- char *index;
- int iscan;
-
- if (*in == '(')
- ++in;
-
- *flag = 0;
-
- for (; *in != '\0'; ++in)
- {
- switch(*in)
- {
-
- /*
- ** take care of csh level of backslashing
- ** for literal !'s, etc.
- */
- case '\\':
- ++in;
- if (*in == '\0')
- {
- diagnostic("Trailing backslash");
- return (0);
- }
- sprintf(out,"\\%c", *in);
- out += 2;
- break;
-
- /*
- ** csh meta-syntax
- */
- case '!':
- *flag = 1;
- if ((iscan = meta(in+1,out)) <= 0)
- return (0);
- in += iscan;
- out += strlen(out);
- break;
-
- /*
- ** set flag for double or single quote escaping
- */
- case '"':
- case '\'':
- *flag = 1; /* fall through */
-
- default:
- *out = *in;
- ++out;
- break;
- }
- }
-
- if (out[-1] == ')')
- --out;
- *out = '\0';
- return (1);
- }
-
- /*
- ** translates what csh meta-syntax can be translated. Returns number
- ** of characters scanned from input, 0 for untranslatable metacharacters
- ** Diagnostics produced at point of discerning error.
- */
- static
- meta(in,buf)
- char *in;
- char *buf;
- {
- int a1, a2;
- int len;
-
- switch (*in)
- {
- case '*':
- strcpy(buf,"$*");
- return (1);
- case '^':
- strcpy(buf,"$1");
- return (1);
- case ':':
- ++in;
- len = 2;
- switch(*in)
- {
- case '*':
- strcpy(buf,"$*");
- return (2);
- case '^':
- a1 = 1;
- break;
- case '-':
- a1 = 0;
- --in;
- len = 1;
- break;
- default:
-
- /*
- ** only handle numeric parameters 0 - 9.
- */
- if (isdigit(*in))
- {
- if (isdigit(in[1]))
- {
- diagnostic("Parameter out of range");
- return(0);
- }
- a1 = *in - '0';
- break;
- }
- diagnostic("Non-numeric '%c' following ':'", *in);
- return (0);
- }
- break;
- default:
- diagnostic("Unknown metacharacter '%c'",*in);
- return (0);
- }
-
- /*
- ** only the : case gets here - a1 is set to number, len to
- ** parsed character count. We now handle ranges. Once again
- ** arguments > $9 are returned as 0.
- */
- ++in;
- if (*in != '-')
- {
- sprintf(buf,"$%d",a1);
- return (len);
- }
- ++in;
- if (! isdigit(*in))
- {
- diagnostic ("Bad parameter range, numeric expected after '-'");
- return (0);
- }
- if (isdigit(in[1]))
- {
- diagnostic("Parameter out of range");
- return(0);
- }
- a2 = *in - '0';
- len += 2;
- if (a2 < a1)
- {
- diagnostic("Bad parameter range");
- return (0);
- }
- for ( ; a1 <= a2; ++a1)
- {
- sprintf(buf,"$%d ",a1);
- buf += 3;
- }
- --buf;
- *buf = '\0';
- return (len);
- }
-
- /*
- ** Some of the sets have meanings assigned to other names in ksh. We
- ** translate these, and simply make unexported assignments for the remainder.
- ** Some of these depend on proper interaction between ksh and the spawned csh,
- ** and are hard to deal with. We usually cheat on CDPATH by attaching the
- ** csh definitions to the ksh ones, since the ksh CDPATH doesn't get passed
- ** to the csh environment.
- */
- static
- set_check(name,setting,f)
- char *name;
- char *setting;
- FILE *f;
- {
- char *colonize();
-
- /*
- ** TERM setting
- */
- if (strcmp(name,"term") == 0)
- {
- var_assign(f,"TERM",setting);
- return;
- }
- /*
- ** pwd is tracked by csh cwd. We set this as a global variable,
- ** generate the cd as the last thing, so that it doesn't matter
- ** whether or not this comes before pushd calls.
- */
- if (strcmp(name,"cwd") == 0)
- {
- strcpy(Pwd,setting);
- return;
- }
- /*
- ** home = HOME
- */
- if (strcmp(name,"home") == 0)
- {
- var_assign(f,"HOME",setting);
- return;
- }
- /*
- ** if we're taking it, prompt = PS1
- */
- if (Doprompt && strcmp(name,"prompt") == 0)
- {
- var_assign(f,"PS1",setting);
- return;
- }
- /*
- ** translate path syntax, place in PATH
- */
- if (strcmp(name,"path") == 0)
- {
- var_assign(f,"PATH",colonize(setting));
- return;
- }
- /*
- ** prepend translated csh cdpath setting to any existing ksh CDPATH
- ** unless Cdreplace flag is set.
- */
- if (strcmp(name,"cdpath") == 0)
- {
- setting = colonize(setting);
- if (Cdreplace)
- var_assign(f,"CDPATH",setting);
- else
- {
- fprintf(f,"if [ -n \"$CDPATH\" ]\nthen\n\tCDPATH=\"");
- dq_out(f,setting);
- fprintf(f,":$CDPATH\"\nelse\n\tCDPATH=\"");
- dq_out(f,setting);
- fprintf(f,"\"\nfi\n");
- }
- return;
- }
- /*
- ** shell (lower case) is probably set to /usr/ucb/csh. set it to
- ** $SHELL (upper case). Don't use var_assign because that will
- ** set it to a literal "$SHELL". This is a "local" variable, so
- ** only do this if Lvar anyway.
- */
- if (Lvar && strcmp(name,"shell") == 0)
- {
- fprintf(f,"shell=\"$SHELL\"\n");
- return;
- }
-
- /*
- ** optional unexported variable
- */
- if (Lvar)
- var_assign(f,name,setting);
- }
-
- /*
- ** Translate csh paths to ksh syntax. Done over top of old string.
- ** A small wrinkle - this routine will result in all pwd's in paths
- ** being represented by '.' instead of an empty entry, because of csh
- ** using "." in describing paths.
- */
- static char *
- colonize(cp)
- char *cp;
- {
- char *ptr;
-
- if (*(ptr = cp) == '(')
- {
- ++cp;
- ++ptr;
- }
-
- /* spaces become colons, terminate on EOS or parentheses */
- for ( ; *cp != '\0' && *cp != ')'; ++cp)
- if (*cp == ' ')
- *cp = ':';
-
- /* terminate string & delete any trailing colons */
- for (*cp = '\0'; cp != ptr && *(--cp) == ':'; *cp = '\0')
- ;
-
- return (ptr);
- }
- SHAR_EOF
- fi # end of overwriting check
- if test -f 'strtok.c'
- then
- echo shar: will not over-write existing file "'strtok.c'"
- else
- cat << \SHAR_EOF > 'strtok.c'
- #include <stdio.h>
-
- static char *Save=NULL;
-
- char *
- strtok(str,delim)
- char *str, *delim;
- {
- char *tokstart, *tokend, *first_ch (), *last_ch();
-
- if (str != NULL)
- Save = str;
-
- if (Save == NULL)
- return (NULL);
-
- tokstart = first_ch (Save, delim);
- tokend = last_ch (tokstart, delim);
- Save = first_ch (tokend, delim);
- *tokend = '\0';
-
- if (*tokstart == '\0')
- return (NULL);
-
- return (tokstart);
- }
-
- static char *
- first_ch (str,delim)
- char *str,*delim;
- {
- char *index ();
- char *f;
-
- for (f = str; *f != '\0' && index(delim,*f) != NULL; ++f)
- ;
-
- return (f);
- }
-
- static char *
- last_ch (str,delim)
- char *str,*delim;
- {
- char *index ();
- char *f;
-
- for (f = str; *f != '\0' && index(delim,*f) == NULL; ++f)
- ;
-
- return (f);
- }
- SHAR_EOF
- fi # end of overwriting check
- if test -f 'func'
- then
- echo shar: will not over-write existing file "'func'"
- else
- cat << \SHAR_EOF > 'func'
- function setenv
- {
- export $1
- eval $1=\"$2\"
- }
-
- function unsetenv
- {
- eval $1=\"\"
- }
-
- KD=/tmp/kd$$
-
- function source
- {
- if [ -s $1 ]
- then
- printenv >$KD.pe
- csh -c "source $1; echo +E >$KD.csh; /usr/ucb/printenv | comm -23 - $KD.pe >>$KD.csh; echo +A >>$KD.csh; alias >>$KD.csh; echo +S >>$KD.csh; set >>$KD.csh; echo +D >>$KD.csh; dirs >>$KD.csh"
- korner $KD.csh $KD.env
- . $KD.env
- if [ -n "$KDOUT" ]
- then
- cat $KD.env >>$KDOUT
- fi
- rm $KD.*
- else
- echo source: $1 not found
- fi
- }
- SHAR_EOF
- fi # end of overwriting check
- # End of shell archive
- exit 0
-